﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.Design;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;

namespace Derbster.Domain
{
    public class Roster : IEnumerable<Player>
    {
        private readonly CommandManager _commandManager;

        [ContractVerification(false)]
        internal Roster()
        {
            
        }

        public Roster(string name, string color)
        {
            Contract.Requires(name != null);
            Contract.Requires(color != null);
            Contract.Ensures(Players != null);

            Color = color;
            Name = name;

            Players = new HashSet<Player>();
            _commandManager = new CommandManager();
        }


        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int RosterId { get; internal set; }
        public string Color { get; internal set; }
        public string Name { get; internal set; }

        public HashSet<Player> Players { get; internal set; }

        public void AddCaptain(Skater skater)
        {
            Contract.Requires(skater != null);
            Contract.Requires(Players != null);
            Contract.Requires(Players.All(p => !p.IsCaptain));
            Contract.Requires(Players.All(p => p.Skater != skater));
            Contract.Ensures(Players.Any(p => p.Skater == skater));
            Contract.Ensures(Players.Any(p => p.IsCaptain));

            DoAddCaptain(skater);
            if (_commandManager != null)
            {
                _commandManager.Add(
                    () => DoRemoveCaptain(),
                    () => DoAddCaptain(skater));
            }
        }

        private void DoAddCaptain(Skater skater)
        {
            Contract.Requires(skater != null);
            Contract.Requires(Players != null);
            Contract.Requires(Players.All(p => !p.IsCaptain));
            Contract.Requires(Players.All(p => p.Skater != skater));
            Contract.Ensures(Players.Any(p => p.Skater == skater));
            Contract.Ensures(Players.Any(p => p.IsCaptain));

            var player = new Player(skater) { IsCaptain = true, Roster = this };
            Players.Add(player);
            
        }

        public void RemoveCaptain()
        {
            Contract.Requires(Players != null);
            Contract.Requires(Players.Count > 0);
            Contract.Requires(Players.Any(p => p.IsCaptain));
            Contract.Ensures(Players.All(p => !p.IsCaptain));

            var player = Players.Single(p => p.IsCaptain);
            Players.Remove(player);

            if (_commandManager != null)
            {
                _commandManager.Add(
                    () => DoAddCaptain(player.Skater),
                    () => DoRemoveCaptain());
            } 
        }
        private void DoRemoveCaptain()
        {
            Contract.Requires(Players != null);
            Contract.Requires(Players.Any(p => p.IsCaptain));
            Contract.Ensures(Players.All(p => !p.IsCaptain));
            Players.RemoveWhere(p => p.IsCaptain);

        }

        public void AddSkater(Skater skater)
        {
            Contract.Requires(skater != null);
            Contract.Requires(Players != null);
            Contract.Requires(Players.All(p => p.Skater != skater));
            Contract.Ensures(Players.Any(p => p.Skater == skater));

            DoAddSkater(skater);

            if (_commandManager != null)
            {
                _commandManager.Add(
                    () => DoRemoveSkater(skater),
                    () => DoAddSkater(skater));
            }
        }

        private void DoAddSkater(Skater skater)
        {
            Contract.Requires(skater != null);
            Contract.Requires(Players != null);
            Contract.Requires(Players.All(p => p.Skater != skater));
            Contract.Ensures(Players.Any(p => p.Skater == skater));

            Players.Add(new Player(skater) {Roster = this});
        }

        public void RemoveSkater(Skater skater)
        {
            Contract.Requires(Players != null);
            Contract.Requires(Players.Any(p => p.Skater == skater));
            Contract.Ensures(Players.All(p => p.Skater != skater));

            DoRemoveSkater(skater);

            if (_commandManager != null)
            {
                _commandManager.Add(
                    () => DoAddSkater(skater),
                    () => DoRemoveSkater(skater));
            }
        }
        private void DoRemoveSkater(Skater skater)
        {
            Contract.Requires(Players != null);
            Contract.Requires(Players.Any(p => p.Skater == skater));
            Contract.Ensures(Players.All(p => p.Skater != skater));
            Players.RemoveWhere(p => p.Skater == skater);
        }
       
        public IEnumerator<Player> GetEnumerator()
        {
            Contract.Assume(Players != null);
            return Players.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }
}
